﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;



namespace BlackStar
{
    public class TextboxContentReader : ContentTypeReader<Textbox>
    {
        protected override Textbox Read(ContentReader input, Textbox existingInstance)
        {
            Textbox result = new Textbox();
            result.ReadContent(input);
            return result;
        }
    }

    public class Textbox : Label
    {
        protected Color myHighlightBack = Color.DarkBlue;
        protected Color myHighlightFore = Color.White;

        protected char myPasswordChar = char.MinValue;

        protected int selectStart = 0;
        protected int selectEnd = 0;
        protected int displayStart = 0;

        protected bool cursorBlink = true;
        protected int cursorTime = 0;
        protected bool selecting = false;

        protected internal override void ReadContent(ContentReader input)
        {
            base.ReadContent(input);
            myHighlightBack = input.ReadColor();
            myHighlightFore = input.ReadColor();
            myPasswordChar = input.ReadChar(); 
        }

        public override void Resize()
        {
            if (layoutSuspended) return;
            base.Resize();
            UpdateSkinRects(gui.theme.Button);
        }

        public override void Draw()
        {
            if (!myVisible) return;

            int ss, se;
            ss = Math.Max(selectStart - displayStart, 0);
            se = Math.Max(selectEnd - displayStart, 0);

            gui.theme.Textbox.Draw("Default", skinRects, this.Color);

            // get the text that is currently visible.
            string currentText;
            float cursorpos;

            currentText = DisplayText();
            cursorpos = gui.theme.Fonts.CursorPos(se, currentText, this.myFont);
            while (cursorpos > myInterior.Width)
            {
                displayStart += 1;
                ss = Math.Max(selectStart - displayStart, 0);
                se = Math.Max(selectEnd - displayStart, 0);
                currentText = DisplayText();
                cursorpos = gui.theme.Fonts.CursorPos(se, currentText, this.myFont);
            }

            if (this.HasFocus() && (cursorBlink || cursorTime > 0))
            {
                // draw the cursor.
                gui.theme.Fonts.DrawCursor(se, currentText, this.myFont, myInterior, this.FontColor);
            }

            gui.theme.Fonts.Draw(myFont, currentText, myInterior, this.FontColor);

            if (se != ss)
            {
                // draw the highlight.
                Vector2 startV = myGui.theme.Fonts.MeasureString(this.myFont, currentText.Substring(0, ss));
                Vector2 endV = myGui.theme.Fonts.MeasureString(this.myFont, currentText.Substring(0, se));
                string highlightText = currentText.Substring(Math.Min(ss, se), Math.Abs(se - ss));

                Rectangle highlightArea = new Rectangle(myInterior.X + (int)Math.Min(startV.X,
                                                        endV.X),
                                                        myInterior.Y, Math.Abs((int)(endV.X - startV.X)),
                                                        myInterior.Height);
                highlightArea.Width -= Math.Max(highlightArea.Right - myInterior.Right, 0);
                //if (highlightArea.Right > interior.Right) highlightArea.Width -= 
                gui.DrawRectangle(highlightArea, this.myHighlightBack);

                // Now Redraw the text highlighted
                gui.theme.Fonts.Draw(myFont, highlightText, highlightArea, myHighlightFore);
            }

            // we call this last because child components should be drawn on top of this.
            DrawComponents();
        }

        protected string DisplayText()
        {
            string disp = myText.Substring(displayStart);
            return (myPasswordChar > char.MinValue) ? new string(myPasswordChar, disp.Length) : disp;
        }

        protected string SelectedText()
        {
            return myText.Substring(Math.Min(selectStart, selectEnd), Math.Abs(selectEnd - selectStart));
        }

        public override void Update(GameTime gameTime)
        {
            this.cursorBlink = ((gameTime.TotalRealTime.Milliseconds % 1000) >= 500);
            cursorTime = Math.Max(0, cursorTime - gameTime.ElapsedGameTime.Milliseconds);

            if (selecting && gui.mouse.LeftButton)
            {
                selectEnd = myGui.theme.Fonts.CursorPos(this.myFont, this.myText, gui.mouse.Location, myInterior) + displayStart;

                if (selectEnd < displayStart) displayStart = selectEnd;
                while (selectEnd < 0) { selectEnd++; displayStart--; }
                displayStart = Math.Max(displayStart, 0);

                selectEnd = Math.Min(selectEnd, myText.Length);
                cursorTime = 500;
            }

            // if the left button is no longer down, stop selecting.
            if (!gui.mouse.LeftButton) { selecting = false; }


            base.Update(gameTime);
        }

        public override void DoMouseDown(MouseEventArgs e)
        {
            // take focus
            myGui.HasFocus = this;
            if (e.Buttons.Left && myText.Length > 0)
            {
                selectEnd = selectStart = myGui.theme.Fonts.CursorPos(this.myFont, this.Text, e.Location, myInterior) + displayStart;
                cursorTime = 500;
                selecting = true;
            }
        }

        public override void DoKeyDown(Keys k)
        {
            string newText = myText;

            switch (k)
            {
                case Keys.Home:
                    selectEnd = 0;
                    displayStart = 0;
                    if (!myGui.keyboard.Shift) selectStart = selectEnd;
                    break;
                case Keys.End:
                    selectEnd = myText.Length;
                    if (!myGui.keyboard.Shift) selectStart = selectEnd;
                    break;
                case Keys.Left:
                    selectEnd -= 1;
                    selectEnd = Math.Max(selectEnd, 0);
                    if (!myGui.keyboard.Shift) { selectStart = selectEnd; }
                    break;
                case Keys.Right:
                    selectEnd += 1;
                    selectEnd = Math.Min(selectEnd, myText.Length);
                    if (!myGui.keyboard.Shift) { selectStart = selectEnd; }
                    break;
                case Keys.Back:
                    if (selectEnd != selectStart)
                    {
                        newText = myText.Remove(Math.Min(selectStart, selectEnd), Math.Abs(selectEnd - selectStart));
                        selectStart = selectEnd = Math.Min(selectStart, selectEnd);
                    }
                    else if (selectStart > 0)
                    {
                        newText = myText.Remove(selectStart - 1, 1);
                        selectStart = selectEnd -= 1;
                    }
                    break;
                case Keys.Delete:
                    if (selectEnd != selectStart)
                    {
                        newText = myText.Remove(Math.Min(selectStart, selectEnd), Math.Abs(selectEnd - selectStart));
                        selectStart = selectEnd = Math.Min(selectStart, selectEnd);
                    }
                    else
                    {
                        if (selectStart < myText.Length)
                        { newText = Text.Remove(selectStart, 1); }
                    }
                    break;
                default:
                    // convert it to keystroke?
                    string aKey = myGui.keyboard.GetChar(k);
                    if (aKey == "") break;
                    newText = myText.Remove(Math.Min(selectStart, selectEnd), Math.Abs(selectEnd - selectStart));
                    selectStart = selectEnd = Math.Min(selectStart, selectEnd);
                    newText = newText.Insert(selectEnd, aKey);
                    selectStart = selectEnd += 1;
                    break;
            }
            cursorTime = 500;

            while (selectEnd < displayStart)
            { displayStart -= 1; selectEnd += 1; selectStart += 1; }

            // we set the text property instead of the private myText
            // so that the Text changed event will fire.
            this.Text = newText;

            OnKeyDown(k);
        }

        [ContentSerializer(Optional=true)]
        public Color HighlightForeColor
        {
            get { return myHighlightFore; }
            set { myHighlightFore = value; }
        }

        [ContentSerializer(Optional = true)]
        public Color HighlightBackColor
        {
            get { return myHighlightBack; }
            set { myHighlightBack = value; }
        }

        [ContentSerializer(Optional = true)]
        public char PasswordChar
        {
            get { return myPasswordChar; }
            set { myPasswordChar = value; }
        }

        public override void DoKeyUp(Keys k)
        {
            OnKeyUp(k);            
        }

        protected override void SetText(string value)
        {
            selectStart = Math.Min(selectStart, value.Length);
            selectEnd = Math.Min(selectEnd, value.Length);

            base.SetText(value);
        }
    }
}
